package edu.mit.compilers.assembly.x64;

import edu.mit.compilers.assembly.x64.basic.X64Entry;
import edu.mit.compilers.assembly.x64.codes.*;
import edu.mit.compilers.ir.type.IRBasicType;
import edu.mit.compilers.lowercode.FieldInfo;
import edu.mit.compilers.lowercode.SymbolTable;
import edu.mit.compilers.lowercode.Value;
import edu.mit.compilers.lowercode.Variable;

public class X64RuntimeChecker {
    public static final int EC_MAX = 2;
    public static String errorHandleLabel(int ec) {
        assert ec > 0 : "not support ec " + ec + ", which is not positive";
        return ".RUNTIME_ERROR" + ec;
    }

    /*
     *  .Label:
     *      leaq error1(%rip),  %rdi
     *      call printf
     *      movq $-ec,   %rdi
     *      call exit
     */

    public static void fillErrorHandlers(X64CodeList appender, X64EntryAllocator allocator) {
        for (int ec = 1; ec <= EC_MAX; ++ec) {
            appender.append(errorHandleCodes(ec, allocator));
        }
    }

    private static X64CodeList errorHandleCodes(int ec, X64EntryAllocator allocator) {
        assert ec > 0 && ec <= EC_MAX : "not support ec " + ec;
        X64CodeList ret = new X64CodeList();
        ret.append(new X64LabelCode(errorHandleLabel(ec)));
        X64Entry src = allocator.allocateGlobal(X64Util.builtInEcName(ec), 8).getEntry();
        X64Entry rdi = allocator.allocateArgRegister(0, 8).getEntry();
        ret.append(new X64LeaCode(src.getLocation(), rdi.getLocation()));
        ret.append(new X64CallCode(allocator.allocateImmediate("printf").getEntry().getLocation()));

        X64Entry ecEntry = allocator.allocateImmediate("$-" + ec).getEntry();
        ret.append(new X64MoveCode(ecEntry.getLocation(), rdi.getLocation()));
        ret.append(new X64CallCode(allocator.allocateImmediate("exit").getEntry().getLocation()));
        return ret;
    }

    public static void fillErrorInfoIntoSymbolTable(SymbolTable symbolTable) {
        for (int ec = 1; ec <= EC_MAX; ++ec) {
            Variable variable = new Variable(X64Util.builtInEcName(ec), true);
            variable.setConstValue(new Value('"' + getErrorInfoContent(ec) + '"'));
            FieldInfo fieldInfo = new FieldInfo(IRBasicType.StringType, variable);
            symbolTable.putConstStringInfo(fieldInfo);
        }
    }

    private static String getErrorInfoContent(int ec) {
        String prefix = "*** RUNTIME ERROR ***: ";
        switch (ec) {
            case 1: return prefix + "Array out of Bounds access in method \\\"%s\\\"\\n";
            case 2: return prefix + "No return value from non-void method \\\"%s\\\"\\n";
            default:
                assert false : "unknown ec " + ec;
                return null;
        }
    }
}
